package net.lab0.fractal.fractals.mandelbrot;

import java.io.File;
import java.io.IOException;

import net.lab0.fractal.exception.InvalidXMLParameterException;
import net.lab0.fractal.fractals.mandelbrot.MandelbrotQuadTreeZoneInfo.NodeStatus;
import net.lab0.tools.geom.Point;
import net.lab0.tools.geom.PointInterface;
import net.lab0.tools.quadtree.AbstractQuadTreeNode;
import net.lab0.tools.quadtree.DataQuadTreeNode;
import net.lab0.tools.quadtree.QuadTreeNode;
import net.lab0.tools.quadtree.QuadTreeRoot;
import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Elements;
import nu.xom.ParsingException;
import nu.xom.ValidityException;

public class MandelbrotQuadTree extends QuadTreeRoot<Point>
{
    private int pointsPerSide;
    private int diffIterLimit;
    
    public MandelbrotQuadTree create(int maxNodeDepth)
    {
        DataQuadTreeNode<Point, MandelbrotQuadTreeZoneInfo> rootNode = new DataQuadTreeNode<Point, MandelbrotQuadTreeZoneInfo>(null, new Point(-2.0d, -2.0d),
                new Point(2.0d, 2.0d));
        MandelbrotQuadTree tree = new MandelbrotQuadTree(maxNodeDepth, rootNode);
        return tree;
    }
    
    private MandelbrotQuadTree(int maxNodeDepth, DataQuadTreeNode<PointInterface, MandelbrotQuadTreeZoneInfo> rootNode)
    {
        super(1, maxNodeDepth, rootNode);
    }
    
    public static MandelbrotQuadTree loadFromXML(File file) throws ValidityException, ParsingException, IOException, InvalidXMLParameterException
    {
        Builder builder = new Builder();
        Document document = builder.build(file);
        
        Element root = document.getRootElement();
        
        String rootName = "MandelbrotQuadTree";
        if (!root.getLocalName().equals(rootName))
        {
            throw new IllegalArgumentException("The root of the XML document must be of type " + rootName);
        }
        
        Element rootNodeElement = root.getFirstChildElement("node");
        
        // check root node position
        Element position = rootNodeElement.getFirstChildElement("position");
        
        String minXString = position.getAttributeValue("minX");
        String maxXString = position.getAttributeValue("maxX");
        String minYString = position.getAttributeValue("minY");
        String maxYString = position.getAttributeValue("maxY");
        
        double minX = Double.parseDouble(minXString);
        double maxX = Double.parseDouble(maxXString);
        double minY = Double.parseDouble(minYString);
        double maxY = Double.parseDouble(maxYString);

        Point p1 = new Point(minX, minY);
        Point p2 = new Point(maxX, maxY);
        
        DataQuadTreeNode<PointInterface, MandelbrotQuadTreeZoneInfo> rootNode = new DataQuadTreeNode<PointInterface, MandelbrotQuadTreeZoneInfo>(p1, p2);
        
        // TODO : maxNodeDepth -> calculer max profondeur de la node racine
        MandelbrotQuadTree tree = new MandelbrotQuadTree(256, rootNode);
        
        recursiveParseNodes(rootNodeElement, rootNode);
        
        String pointPerSideString = root.getAttributeValue("pointsPerSide");
        String diffIterLimitString = root.getAttributeValue("diffIterLimit");
        
        tree.pointsPerSide = Integer.parseInt(pointPerSideString);
        tree.diffIterLimit = Integer.parseInt(diffIterLimitString);
        
        if (tree.pointsPerSide < 2)
        {
            throw new InvalidXMLParameterException("pointsPerSide must be >= 2");
        }
        
        if (tree.diffIterLimit < 1)
        {
            throw new InvalidXMLParameterException("diffIterLimit must be >= 1");
        }
        
        return tree;
    }
    
    private static void recursiveParseNodes(Element xmlNode, DataQuadTreeNode<Point, MandelbrotQuadTreeZoneInfo> treeNode) throws InvalidXMLParameterException
    {
        String nodePath = xmlNode.getAttributeValue("path");
        Element info = xmlNode.getFirstChildElement("infos");
        String statusString = info.getAttributeValue("status");
        int statusValue = Integer.parseInt(statusString);
        NodeStatus status = null;
        switch (statusValue)
        {
            case 0:
                status = NodeStatus.VOID;
                break;
            
            case 1:
                status = NodeStatus.BROWSED;
                break;
            
            case 2:
                status = NodeStatus.INSIDE;
                break;
            
            case 3:
                status = NodeStatus.OUTSIDE;
                break;
            
            default:
                throw new InvalidXMLParameterException("status value must be in the range [0-3] included");
        }
        
        MandelbrotQuadTreeZoneInfo mandelbrotQuadTreeZoneInfo = null;
        if (status == NodeStatus.OUTSIDE)
        {
            String minIterString = info.getAttributeValue("minIter");
            String maxIterString = info.getAttributeValue("maxIter");
            
            long minIter = Long.parseLong(minIterString);
            long maxIter = Long.parseLong(maxIterString);
            
            mandelbrotQuadTreeZoneInfo = new MandelbrotQuadTreeZoneInfo(status, minIter, maxIter);
        }
        else
        {
            mandelbrotQuadTreeZoneInfo = new MandelbrotQuadTreeZoneInfo(status, -1, -1);
        }
        
        treeNode.setNodeElement(mandelbrotQuadTreeZoneInfo);
        
        Element children = xmlNode.getFirstChildElement("node");
        if (children != null)
        {
            if (treeNode.isLeafNode())
            {
                treeNode.split();
            }
            
            Elements xmlChildren = xmlNode.getChildElements("node");
            if (xmlChildren.size() != 4)
            {
                throw new InvalidXMLParameterException("A node must have 4 children : error at node " + nodePath);
            }
            
            for (int i = 0; i < 4; ++i)
            {
                recursiveParseNodes(xmlChildren.get(i), treeNode.getNode(i));
            }
        }
    }
    
    public static void main(String[] args) throws ValidityException, ParsingException, IOException, InvalidXMLParameterException
    {
        System.out.println("start");
        MandelbrotQuadTree testTree = MandelbrotQuadTree.loadFromXML(new File("C:\\Users\\116\\workspace\\CMandelbrotTree\\out\\out.xml"));
        System.out.println("end");
    }
}
